// This is unpublished source code. Michah Lerner 2006

package trieMatch.keywordMatcher;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import trieMatch.Interfaces.AtomFactory;
import trieMatch.Interfaces.Element;

/**
 * A StructuredElement provides an new sequence ordering, while also preserving the original sequence order. This
 * implementation represents the new order by placing the atoms into a list. The previous order of each atom is
 * preserved through an offset stored within each atom.
 * 
 * 
 * @author Michah.Lerner
 * 
 */
public class StructuredElement extends ArrayList<Atom> implements Element<Atom> {

	private static final long serialVersionUID = 1L;
	private static Map<Collection<String>, StructuredElement> primalElements = new HashMap<Collection<String>, StructuredElement>();
	static AtomFactory atomFactory;
	static {
		try {
			atomFactory = (AtomFactory) Class.forName(trieMatch.Constants.AtomFactoryName).newInstance();
		} catch (final InstantiationException e) {
			e.printStackTrace();
		} catch (final IllegalAccessException e) {
			e.printStackTrace();
		} catch (final ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	/*******************************************************************************************************************
	 * Construct a structured element from a collection of Strings.
	 * 
	 * @param tokens
	 */
	StructuredElement(final Collection<String> tokens) {
		int index = 0;
		for (final String token : tokens) {
			add(atomFactory.mkAtom(token, index++));
		}
		arrangeAtoms();
	}

	/*******************************************************************************************************************
	 * Static constructor
	 * 
	 * @param tokens
	 * @return a structured element
	 */
	static StructuredElement mkStructuredElement(final Collection<String> tokens) {
		final StructuredElement structuredElement = primalElements.get(tokens);
		return structuredElement == null
				? new StructuredElement(tokens)
				: structuredElement;
	}

	static StructuredElement mkStructuredElement(final String[] tokens) {
		return mkStructuredElement(Arrays.asList(tokens));
	}

	/*******************************************************************************************************************
	 * Get the string value of the n<i>th</i> atom of the StruturedElement.
	 */
	public String getValue(final int i) {
		final String s = this.get(i).getValue();
		return s;
	}

	/**
	 * Get the original index offset of the n<i>th</i> atom of the StruturedElement.
	 */
	public int getIndex(final int i) {
		return this.get(i).getIndex();
	}

	/**
	 * Place the Atoms into sorted (i.e., lexicographic) order
	 */
	public void arrangeAtoms() {
		this.trimToSize();
		Collections.sort(this);
	}

	public String describe() {
		final StringBuilder sb = new StringBuilder(80);
		int cnt = 0;
		for (final Atom atom : this) {
			sb.append(cnt++ + ":" + atom.getIndex() + "/" + atom.getValue() + " ");
		}
		if (sb.length() > 0) {
			sb.deleteCharAt(sb.length() - 1);
		}
		return sb.toString();
	}

	public String asString() {
		final StringBuilder sb = new StringBuilder(80);
		for (final Atom atom : this) {
			sb.append(atom.getValue() + " ");
		}
		if (sb.length() > 0) {
			sb.deleteCharAt(sb.length() - 1);
		}
		return sb.toString();
	}

	@Override
	public String toString() {
		return describe();
	}

	public Collection<String> allValues() {
		final ArrayList<String> cs = new ArrayList<String>(this.size());
		for (final Atom atom : this) {
			cs.add(atom.getValue());
		}
		return cs;
	}
}
